/*
 * Copyright (c) 2011
 * Thomas Rozanski, Geoffrey Wright, Zachary Lynn, Jesse Natalie
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the team nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THOMAS ROZANSKI, GEOFFREY WRIGHT,
 * ZACHARY LYNN, OR JESSE NATALIE BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.Windows.Forms;
using Kinected;

#if RELEASE_TIMER
using System.Diagnostics;
#endif

/// <summary>A basic Tower Defense game that uses the Kinected Framework.</summary>
namespace TowerDefense
{
	/// <summary>Initializes and runs the sample tower defense game.</summary>
    public class Game : Microsoft.Xna.Framework.Game
    {
		// Kinected framework
		private Kinect kinect; // Kinect
		private Board board; // Game board

		private Overlay overlay;
		private Texture2D overlayTexture;

		private Controller controller;
		private TowerDefense towerDefense;

		// Timers
		private Timer updateTimer, drawTimer;

		private SpriteFont debugFont;

		private GraphicsDeviceManager graphics;
		private SpriteBatch spriteBatch;

		private int ScreenWidth = 1280;
		private int ScreenHeight = 720;

        public Game()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

			graphics.PreferredBackBufferWidth = ScreenWidth;
			graphics.PreferredBackBufferHeight = ScreenHeight;
			graphics.IsFullScreen = false;
        }

        ~Game()
        {
        }

		/// <summary>
		/// Allows the game to perform any initialization it needs to before starting to run. This is where it can
		/// query for any required services and load any non-graphic related content. Calling base.Initialize will
		/// enumerate through any components and initialize them as well.
		/// </summary>
        protected override void Initialize()
        {
			int kinectCount = Kinect.GetDeviceCount();

			// no devices
			if (kinectCount == 0)
			{
				if (MessageBox.Show("A Microsoft Kinect was not detected. Would you like to use a mock Kinect instead?",
					"Microsoft Kinect not found.", MessageBoxButtons.YesNo) == DialogResult.No)
				{
					MessageBox.Show("Program cannot be run without a Kinect.", "Error!");
					this.Exit();
				}
				kinect = new Kinect();
			}
			// a device
			else
			{
				try
				{
					kinect = new Kinect(Kinect.GetDeviceSerial(0));
				}
				catch (KinectCreationException)
				{
					if (MessageBox.Show("The program was unable to connect to the Microsoft Kinect. Would you like to use a mock Kinect instead?",
						"Microsoft Kinect error.", MessageBoxButtons.YesNo) == DialogResult.No)
					{
						MessageBox.Show("Program cannot be run without a Kinect.", "Error!");
						this.Exit();
					}
					kinect = new Kinect();
				}
			}

			RAWImage rawImage = kinect.Camera.GetRAWDepthImage();

            board = new Board(256, ScreenWidth, ScreenHeight); // allow 256 total circles
			board.Initialize(rawImage);

			controller = new Controller();
            towerDefense = new TowerDefense(20); // max enemies

            // Initialize timers
            updateTimer = new Timer();
            drawTimer = new Timer();

			overlay = new Overlay(GraphicsDevice, rawImage.Width, rawImage.Height);

            base.Initialize();
        }

        /// <summary>LoadContent will be called once per game and is the place to load all of your content.</summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            debugFont = Content.Load<SpriteFont>("interface/debug");
            overlayTexture = Content.Load<Texture2D>("interface/overlayFrame");

			towerDefense.LoadContent(Content);
            overlay.LoadContent(Content);
        }

		/// <summary>UnloadContent will be called once per game and is the place to unload all content. </summary>
		protected override void UnloadContent()
        {
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
			updateTimer.Start();

			// Update the controller
			controller.Update(gameTime);

			// Allows the game to exit
			if (controller.KeyPress(Microsoft.Xna.Framework.Input.Keys.Escape, Buttons.Back))
			{
				this.Exit();
			}

			bool haveNewCameraImage = kinect.Camera.HasNewImage();

			overlay.Update(controller, kinect.Camera.GetRGBAColorImage(), kinect.Camera.GetRGBADepthImage(), haveNewCameraImage);
			board.Update(gameTime, kinect.Camera.GetRAWDepthImage(), haveNewCameraImage);
			towerDefense.Update(gameTime, board);

            base.Update(gameTime);

            updateTimer.End();
        }

		/// <summary>This is called when the game should draw itself.</summary>
		/// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            drawTimer.Start();

            GraphicsDevice.Clear(Color.Black);

            spriteBatch.Begin();

            towerDefense.Draw(spriteBatch);
            overlay.Draw(spriteBatch);

            spriteBatch.DrawString(debugFont, "Updated in " + updateTimer.Elapsed() + "ms", new Vector2(5, 0), Color.White);
            spriteBatch.DrawString(debugFont, "Drawn in " + drawTimer.Elapsed() + "ms", new Vector2(5, 20), Color.White);
            spriteBatch.DrawString(debugFont, "Background depth = " + board.BackgroundDepthValue.ToString(), new Vector2(5, 40), Color.White);
            spriteBatch.DrawString(debugFont, "Difference = " + board.BoardIsChanging.ToString(), new Vector2(5, 60), Color.White);

            spriteBatch.End();

            drawTimer.End();

            base.Draw(gameTime);
        }
    }
}
